home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vb4comms / dwcomm.cls < prev    next >
Encoding:
Text File  |  1996-03-27  |  19.0 KB  |  524 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4. END
  5. Attribute VB_Name = "dwComm"
  6. Attribute VB_Creatable = True
  7. Attribute VB_Exposed = False
  8. ' dwDCB - Device Communication Block utility class
  9. ' Part of the Desaware API Class Library
  10. ' Copyright (c) 1996 by Desaware.
  11. ' All Rights Reserved
  12. Option Explicit
  13.  
  14. Private Type COMMTIMEOUTS
  15.         ReadIntervalTimeout As Long
  16.         ReadTotalTimeoutMultiplier As Long
  17.         ReadTotalTimeoutConstant As Long
  18.         WriteTotalTimeoutMultiplier As Long
  19.         WriteTotalTimeoutConstant As Long
  20. End Type
  21.  
  22. Private Type OVERLAPPED
  23.         Internal As Long
  24.         InternalHigh As Long
  25.         offset As Long
  26.         OffsetHigh As Long
  27.         hEvent As Long
  28. End Type
  29.  
  30. ' Private members
  31. Private timeouts As COMMTIMEOUTS
  32. Private handle As Long  ' Comm handle
  33. Private devname$ ' Com1, com2 or other compatible comm device
  34.  
  35. ' Public members
  36. Public DCB As dwDCB
  37.  
  38. ' Current state indicators
  39. ' Holds output data that arrives while an output transfer is in progress
  40. Private PendingOutput$
  41. Private CurrentEventMask&   ' Non zero if events are being watched for
  42. ' Buffers for overlapped input and output
  43. ' Must take this approach due to VB's ability to move strings
  44. Private CurrentInputBuffer&
  45. Private CurrentOutputBuffer&
  46. Private TriggeredEvents&    ' Variable to load with event results
  47.  
  48. ' Three overlapped structures,
  49. ' 0 = read, 1 = write, 2 = waitevent
  50. Private overlaps(2) As OVERLAPPED
  51. ' Indicates background operation is in progress
  52. Private inprogress(2) As Boolean
  53. ' Amount of data transferred on write
  54. Private DataWritten&
  55. Private DataRead&
  56. Private EventResults&
  57.  
  58. ' This object must have two functions
  59. ' CommInput(dev As dwComm, info As String)
  60. ' CommEvent(dev As dwComm, event as long)
  61. Private CallbackObject As Object
  62.  
  63. ' Declarations
  64. Private Declare Function apiSetCommTimeouts Lib "kernel32" Alias "SetCommTimeouts" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long
  65. Private Declare Function apiGetCommTimeouts Lib "kernel32" Alias "GetCommTimeouts" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long
  66. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
  67. Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
  68. Private Declare Function SetupComm Lib "kernel32" (ByVal hFile As Long, ByVal dwInQueue As Long, ByVal dwOutQueue As Long) As Long
  69. Private Declare Function GetCommModemStatus Lib "kernel32" (ByVal hFile As Long, lpModemStat As Long) As Long
  70. Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
  71. Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
  72. Private Declare Function lstrcpyFromBuffer Lib "kernel32" Alias "lstrcpynA" (ByVal lpString1 As String, ByVal buffer As Long, ByVal iMaxLength As Long) As Long
  73. Private Declare Function lstrcpyToBuffer Lib "kernel32" Alias "lstrcpynA" (ByVal buffer As Long, ByVal lpString2 As String, ByVal iMaxLength As Long) As Long
  74. Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long
  75. Private Declare Function GetLastError Lib "kernel32" () As Long
  76. Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (ByVal lpEventAttributes As Long, ByVal bManualReset As Long, ByVal bInitialState As Long, ByVal lpName As String) As Long
  77. Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
  78. Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long
  79. Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long
  80. Private Declare Function SetCommMask Lib "kernel32" (ByVal hFile As Long, ByVal dwEvtMask As Long) As Long
  81. Private Declare Function ClearCommError Lib "kernel32" (ByVal hFile As Long, lpErrors As Long, ByVal l As Long) As Long
  82. Private Declare Function WaitCommEvent Lib "kernel32" (ByVal hFile As Long, lpEvtMask As Long, lpOverlapped As OVERLAPPED) As Long
  83.  
  84. Private Const GENERIC_READ = &H80000000
  85. Private Const GENERIC_WRITE = &H40000000
  86. Private Const OPEN_EXISTING = 3
  87. Private Const FILE_FLAG_OVERLAPPED = &H40000000
  88. Private Const INVALID_HANDLE_VALUE = -1
  89. Private Const GMEM_FIXED = &H0
  90. Private Const ClassBufferSizes% = 1024
  91. Private Const ERROR_IO_PENDING = 997 '  dderror
  92. Private Const WAIT_TIMEOUT = &H102&
  93.  
  94. ' GetCommModemStatus flags
  95. Private Const MS_CTS_ON = &H10&
  96. Private Const MS_DSR_ON = &H20&
  97. Private Const MS_RING_ON = &H40&
  98. Private Const MS_RLSD_ON = &H80&
  99.  
  100. ' Error values
  101. Private Const CLASS_NAME$ = "dwComm"
  102. Private Const ERR_NOCOMMACCESS = 31010
  103. Private Const ERR_UNINITIALIZED = 31011
  104. Private Const ERR_MODEMSTATUS = 31012
  105. Private Const ERR_READFAIL = 31013
  106. Private Const ERR_EVENTFAIL = 31014
  107.  
  108. Private Const EV_RXCHAR = &H1                '  Any Character received
  109. Private Const EV_RXFLAG = &H2                '  Received certain character
  110. Private Const EV_TXEMPTY = &H4               '  Transmitt Queue Empty
  111. Private Const EV_CTS = &H8                   '  CTS changed state
  112. Private Const EV_DSR = &H10                  '  DSR changed state
  113. Private Const EV_RLSD = &H20                 '  RLSD changed state
  114. Private Const EV_BREAK = &H40                '  BREAK received
  115. Private Const EV_ERR = &H80                  '  Line status error occurred
  116. Private Const EV_RING = &H100                '  Ring signal detected
  117. Private Const EV_PERR = &H200                '  Printer error occured
  118. Private Const EV_RX80FULL = &H400            '  Receive buffer is 80 percent full
  119. Private Const EV_EVENT1 = &H800              '  Provider specific event 1
  120. Private Const EV_EVENT2 = &H1000             '  Provider specific event 2
  121.  
  122. Private Const CE_RXOVER = &H1                '  Receive Queue overflow
  123. Private Const CE_OVERRUN = &H2               '  Receive Overrun Error
  124. Private Const CE_RXPARITY = &H4              '  Receive Parity Error
  125. Private Const CE_FRAME = &H8                 '  Receive Framing error
  126. Private Const CE_BREAK = &H10                '  Break Detected
  127. Private Const CE_TXFULL = &H100              '  TX Queue is full
  128.  
  129. ' An empty string with a single null character
  130. Private EmptyString As String * 1
  131.  
  132. Private Sub Class_Initialize()
  133.     Dim olnum%
  134.     Set DCB = New dwDCB
  135.     CurrentInputBuffer = GlobalAlloc(GMEM_FIXED, ClassBufferSizes + 1)
  136.     CurrentOutputBuffer = GlobalAlloc(GMEM_FIXED, ClassBufferSizes + 1)
  137.     CurrentEventMask = EV_ERR
  138.     EmptyString = Chr$(0)
  139.     ' Create event objects for the background transfer
  140.     For olnum = 0 To 2
  141.         overlaps(olnum).hEvent = CreateEvent(0, True, False, vbNullString)
  142.     Next olnum
  143. End Sub
  144.  
  145. Private Sub Class_Terminate()
  146.     Dim olnum
  147.     ' Close existing comm device
  148.     Call CloseComm
  149.     ' Dump the event objects
  150.     For olnum = 0 To 2
  151.         Call CloseHandle(overlaps(olnum).hEvent)
  152.     Next olnum
  153.     Set DCB = Nothing   ' Be sure DCB is free
  154.     Call GlobalFree(CurrentInputBuffer)
  155.     Call GlobalFree(CurrentOutputBuffer)
  156.     
  157. End Sub
  158.  
  159. ' Useful error routines
  160. Private Sub DeviceNotOpenedError()
  161.     Err.Raise vbObjectError + ERR_UNINITIALIZED, CLASS_NAME, "Communication Device is not open"
  162. End Sub
  163.  
  164. Private Sub ModemStatusError()
  165.     Err.Raise vbObjectError + ERR_MODEMSTATUS, CLASS_NAME, "GetCommModemStatus Failed"
  166. End Sub
  167.  
  168.  
  169. '-----------------------------------------------
  170. ' Timeout property access follows
  171. '-----------------------------------------------
  172. Public Property Get ReadIntervalTimeout() As Long
  173.     ReadIntervalTimeout = timeouts.ReadIntervalTimeout
  174. End Property
  175.  
  176. Public Property Let ReadIntervalTimeout(vNewValue As Long)
  177.     timeouts.ReadIntervalTimeout = vNewValue
  178. End Property
  179.  
  180. Public Property Get ReadTotalTimeoutMultiplier() As Long
  181.     ReadTotalTimeoutMultiplier = timeouts.ReadTotalTimeoutMultiplier
  182. End Property
  183.  
  184. Public Property Let ReadTotalTimeoutMultiplier(vNewValue As Long)
  185.     timeouts.ReadTotalTimeoutMultiplier = vNewValue
  186. End Property
  187.  
  188.  
  189. Public Property Get ReadTotalTimeoutConstant() As Long
  190.     ReadTotalTimeoutConstant = timeouts.ReadTotalTimeoutConstant
  191. End Property
  192.  
  193. Public Property Let ReadTotalTimeoutConstant(vNewValue As Long)
  194.     timeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant
  195. End Property
  196.  
  197. Public Property Get WriteTotalTimeoutMultiplier() As Long
  198.     WriteTotalTimeoutMultiplier = timeouts.WriteTotalTimeoutMultiplier
  199. End Property
  200.  
  201. Public Property Let WriteTotalTimeoutMultiplier(vNewValue As Long)
  202.     timeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier
  203. End Property
  204.  
  205. Public Property Get WriteTotalTimeoutConstant() As Long
  206.     WriteTotalTimeoutConstant = timeouts.WriteTotalTimeoutConstant
  207. End Property
  208.  
  209. Public Property Let WriteTotalTimeoutConstant(vNewValue As Long)
  210.     timeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant
  211. End Property
  212.  
  213. ' The device handle is read only
  214. Public Property Get hCommDev() As Long
  215.     hCommDev = handle
  216. End Property
  217.  
  218. ' This property is read only
  219. Public Property Get DeviceName() As String
  220.     DeviceName = devname
  221. End Property
  222.  
  223. Public Sub GetCommTimeouts()
  224.     ' Is there any real need to report errors here?
  225.     If handle = 0 Then Exit Sub
  226.     Call apiGetCommTimeouts(handle, timeouts)
  227. End Sub
  228.  
  229. Public Function SetCommTimeouts() As Long
  230.     If handle = 0 Then Exit Function ' Returns false
  231.     SetCommTimeouts = apiSetCommTimeouts(handle, timeouts) <> 0
  232. End Function
  233.  
  234. ' The main function for opening a comm device
  235. ' Receives device name (com?) and optionally the size of the internal input and output queues
  236. Public Function OpenComm(CommDeviceName As String, Notify As Object, Optional cbInQueue, Optional cbOutQueue) As Long
  237.     ' Close an existing port when reopening
  238.     If handle <> 0 Then CloseComm
  239.     devname = CommDeviceName
  240.     Set CallbackObject = Notify
  241.     handle = CreateFile(devname, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)
  242.     If handle = INVALID_HANDLE_VALUE Then Err.Raise vbObjectError + ERR_NOCOMMACCESS, CLASS_NAME, "Unable to open communications device"
  243.     ' If the input and output queue size is specified, set it now
  244.     If Not (IsMissing(cbInQueue) Or IsMissing(cbOutQueue)) Then
  245.         Call SetupComm(handle, cbInQueue, cbOutQueue)
  246.     Else
  247.         Call SetupComm(handle, 8192, 1024)
  248.     End If
  249.     ' Ok, we've got the comm port. Initialize the timeouts
  250.     GetCommTimeouts
  251.     ' Set some default timeouts
  252.     timeouts.ReadIntervalTimeout = 1
  253.     timeouts.ReadTotalTimeoutMultiplier = 0
  254.     timeouts.ReadTotalTimeoutConstant = 10
  255.     timeouts.WriteTotalTimeoutMultiplier = 1
  256.     timeouts.WriteTotalTimeoutConstant = 1
  257.     SetCommTimeouts
  258.     ' Initialize the DCB to the current device parameters
  259.     Call DCB.GetCommState(Me)
  260.     Call SetCommMask(handle, CurrentEventMask)
  261.     StartInput
  262. End Function
  263.  
  264. ' Close the comm port
  265. Public Function CloseComm() As Long
  266.     ' Already closed, just exit
  267.     If handle = 0 Then Exit Function
  268.     Set CallbackObject = Nothing
  269.     Call CloseHandle(handle)
  270.     handle = 0
  271. End Function
  272.  
  273.  
  274.  
  275. ' This is another entry to retreive the comm state
  276. ' Note how it handles the problem of DCB needing the
  277. ' dwComm object parameter
  278. Public Function GetCommState() As Long
  279.     If handle = 0 Then DeviceNotOpenedError
  280.     GetCommState = DCB.GetCommState(Me)
  281. End Function
  282.  
  283. ' This is another entry to retrieve the comm state
  284. ' Note how it handles the problem of DCB needing the
  285. ' dwComm object parameter
  286. Public Function SetCommState() As Long
  287.     If handle = 0 Then DeviceNotOpenedError
  288.     SetCommState = DCB.SetCommState(Me)
  289. End Function
  290.  
  291. ' Here are some easy functions to determine the current
  292. ' modem status
  293.  
  294. Public Property Get CTS_ON()
  295.     Dim modemstatus&
  296.     Dim res&
  297.     If handle = 0 Then DeviceNotOpenedError
  298.     res = GetCommModemStatus(handle, modemstatus)
  299.     If res = 0 Then ModemStatusError
  300.     CTS_ON = (modemstatus And MS_CTS_ON) <> 0
  301. End Property
  302.  
  303.  
  304. Public Property Get DSR_ON()
  305.     Dim modemstatus&
  306.     Dim res&
  307.     If handle = 0 Then DeviceNotOpenedError
  308.     res = GetCommModemStatus(handle, modemstatus)
  309.     If res = 0 Then ModemStatusError
  310.     DSR_ON = (modemstatus And MS_DSR_ON) <> 0
  311.  
  312. End Property
  313.  
  314.  
  315. Public Property Get RING_ON()
  316.     Dim modemstatus&
  317.     Dim res&
  318.     If handle = 0 Then DeviceNotOpenedError
  319.     res = GetCommModemStatus(handle, modemstatus)
  320.     If res = 0 Then ModemStatusError
  321.     RING_ON = (modemstatus And MS_RING_ON) <> 0
  322.  
  323. End Property
  324.  
  325.  
  326. Public Property Get RLSD_ON()
  327.     Dim modemstatus&
  328.     Dim res&
  329.     If handle = 0 Then DeviceNotOpenedError
  330.     res = GetCommModemStatus(handle, modemstatus)
  331.     If res = 0 Then ModemStatusError
  332.     RLSD_ON = (modemstatus And MS_RLSD_ON) <> 0
  333. End Property
  334.  
  335.  
  336.  
  337. ' Data output Function
  338. Public Function CommOutput(outputdata As String) As Long
  339.     Dim bytestosend&
  340.     Dim res&
  341.     Dim addnull As Boolean
  342.     If handle = 0 Then DeviceNotOpenedError
  343.     PendingOutput = PendingOutput & outputdata
  344.     If inprogress(1) Then    ' Write operation is in progress
  345.         CommOutput = True
  346.         Exit Function
  347.     End If
  348.     ' Start a new output operation
  349.     bytestosend = Len(PendingOutput)
  350.     ' No data to send, just exit
  351.     If bytestosend = 0 Then
  352.         CommOutput = True
  353.         Exit Function
  354.     End If
  355.     ' Don't overflow our buffer
  356.     If bytestosend > ClassBufferSizes Then bytestosend = ClassBufferSizes
  357.     ' If there is a null character, just send up to the null
  358.     If lstrlen(PendingOutput) < bytestosend Then
  359.         ' but mark that we should send the null as well
  360.         bytestosend = lstrlen(PendingOutput)
  361.         addnull = True
  362.     End If
  363.     
  364.     If bytestosend > 0 Then Call lstrcpyToBuffer(CurrentOutputBuffer, PendingOutput, bytestosend + 1)
  365.     If bytestosend = Len(PendingOutput) Then
  366.         PendingOutput = ""
  367.     Else
  368.         PendingOutput = Mid(PendingOutput, bytestosend + 1)
  369.     End If
  370.     If addnull Then bytestosend = bytestosend + 1
  371.     res = WriteFile(handle, CurrentOutputBuffer, bytestosend, DataWritten, overlaps(1))
  372.     If res <> 0 Then
  373.         ProcessWriteComplete
  374.         CommOutput = True
  375.     Else
  376.         If GetLastError() = ERROR_IO_PENDING Then
  377.             inprogress(1) = True
  378.             CommOutput = True
  379.             #If DEBUGMODE Then
  380.                Debug.Print "Pended write"
  381.             #End If
  382.         End If
  383.     End If
  384. End Function
  385.  
  386. ' Restart the next output operation if necessary
  387. Public Sub ProcessWriteComplete()
  388.     inprogress(1) = False
  389.     Call CommOutput("")
  390. End Sub
  391.  
  392. ' Called periodically
  393. Public Sub PollWrite()
  394.     Dim res&
  395.     If Not inprogress(1) Then Exit Sub
  396.     ' Check the event
  397.     res = WaitForSingleObject(overlaps(1).hEvent, 0)
  398.     ' If not yet signaled, just exit
  399.     If res = WAIT_TIMEOUT Then Exit Sub
  400.     ' Data was written - Try writing any pending data
  401.     ProcessWriteComplete
  402. End Sub
  403.  
  404. ' This function enables or disables data transfer
  405. Private Sub StartInput()
  406.     Dim res&
  407.     ' Read already in progress
  408.     If inprogress(0) Then Exit Sub
  409.     If handle = 0 Then DeviceNotOpenedError
  410.     res = ReadFile(handle, CurrentInputBuffer, ClassBufferSizes, DataRead, overlaps(0))
  411.     If res <> 0 Then
  412.         ProcessReadComplete
  413.     Else
  414.         If GetLastError() = ERROR_IO_PENDING Then
  415.             inprogress(0) = True
  416.             #If DEBUGMODE Then
  417.                Debug.Print "pended read"
  418.             #End If
  419.         Else
  420.             Err.Raise vbObjectError + ERR_READFAIL, CLASS_NAME, "Failure on Comm device read operation"
  421.         End If
  422.     End If
  423. End Sub
  424.  
  425. Public Sub PollRead()
  426.     Dim res&
  427.     If Not inprogress(0) Then
  428.         StartInput
  429.         Exit Sub
  430.     End If
  431.         
  432.     ' Check the event
  433.     res = WaitForSingleObject(overlaps(0).hEvent, 0)
  434.     ' If not yet signaled, just exit
  435.     If res = WAIT_TIMEOUT Then Exit Sub
  436.     ' Data was written - Try writing any pending data
  437.     ProcessReadComplete
  438. End Sub
  439.  
  440. Public Sub ProcessReadComplete()
  441.     Dim resstring$
  442.     Dim copied&
  443.     If inprogress(0) Then ' Was overlapped
  444.         DataRead = overlaps(0).InternalHigh
  445.         inprogress(0) = False
  446.     End If
  447.     If DataRead <> 0 Then
  448.       #If DEBUGMODE Then
  449.         Debug.Print "Read " & DataRead & " bytes"
  450.       #End If
  451.         resstring$ = String$(DataRead + 1, 0)
  452.         copied = lstrcpyFromBuffer(resstring, CurrentInputBuffer, DataRead + 1)
  453.         If Not (CallbackObject Is Nothing) Then
  454.             Call CallbackObject.CommInput(Me, Left$(resstring, DataRead))
  455.         End If
  456.     End If
  457. End Sub
  458.  
  459. Private Sub StartEventWatch()
  460.     Dim res&
  461.     ' Read already in progress
  462.     If inprogress(2) Then Exit Sub
  463.     If handle = 0 Then DeviceNotOpenedError
  464.     EventResults = 0
  465.     res = WaitCommEvent(handle, EventResults, overlaps(2))
  466.     If res <> 0 Then
  467.         ProcessEventComplete
  468.     Else
  469.         If GetLastError() = ERROR_IO_PENDING Then
  470.             inprogress(2) = True
  471.             #If DEBUGMODE Then
  472.                Debug.Print "pended event"
  473.             #End If
  474.         Else
  475.             Err.Raise vbObjectError + ERR_EVENTFAIL, CLASS_NAME, "Failure on Comm device event test operation"
  476.         End If
  477.     End If
  478. End Sub
  479.  
  480. Private Sub ProcessEventComplete()
  481.     Dim errors&
  482.     If inprogress(2) Then ' Was overlapped
  483.         inprogress(2) = False
  484.     End If
  485.     
  486.     If EventResults <> 0 Then
  487.         #If DEBUGMODE Then
  488.          Debug.Print "Event value " & Hex$(EventResults)
  489.          #End If
  490.         If Not (CallbackObject Is Nothing) Then
  491.             Call ClearCommError(handle, errors, 0)
  492.                 
  493.             If (errors And CE_RXOVER) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Queue Full Error")
  494.             If (errors And CE_OVERRUN) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Overrun Error")
  495.             If (errors And CE_RXPARITY) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Parity Error")
  496.             If (errors And CE_FRAME) <> 0 Then Call CallbackObject.CommEvent(Me, "Frame Error")
  497.             If (errors And CE_BREAK) <> 0 Then Call CallbackObject.CommEvent(Me, "Break Detected")
  498.             If (errors And CE_TXFULL) <> 0 Then Call CallbackObject.CommEvent(Me, "Transmit Queue Full")
  499.         End If
  500.     End If
  501. End Sub
  502.  
  503. Private Sub PollEvent()
  504.     Dim res&
  505.     If Not inprogress(2) Then
  506.         StartEventWatch
  507.         Exit Sub
  508.     End If
  509.         
  510.     ' Check the event
  511.     res = WaitForSingleObject(overlaps(2).hEvent, 0)
  512.     ' If not yet signaled, just exit
  513.     If res = WAIT_TIMEOUT Then Exit Sub
  514.     ' Data was written - Try writing any pending data
  515.     ProcessEventComplete
  516. End Sub
  517.  
  518. ' Test results on all background processes
  519. Public Sub Poll()
  520.     PollWrite
  521.     PollRead
  522.     PollEvent
  523. End Sub
  524.